package com.ruyiadmin.springboot.common.utils.system;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.ruyiadmin.springboot.common.beans.core.DatasourceConfig;
import com.ruyiadmin.springboot.common.beans.system.CodeGeneratorConfig;
import com.ruyiadmin.springboot.common.beans.system.DirectoryConfig;
import com.ruyiadmin.springboot.domain.dto.system.DbSchemaEntityDTO;
import com.ruyiadmin.springboot.domain.dto.system.DbSchemaFieldDTO;
import com.ruyiadmin.springboot.domain.dto.system.DbSchemaInfoDTO;
import com.ruyiadmin.springboot.repository.system.ICodeGeneratorRepository;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 代码生成器工具类
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-08-06
 */
@Component
public class RuYiCodeUtil {

    //region 工具类私有属性

    @Resource
    private DatasourceConfig datasourceConfig;
    @Resource
    private DirectoryConfig directoryConfig;
    @Resource
    private ICodeGeneratorRepository codeGeneratorRepository;
    @Resource
    private CodeGeneratorConfig codeGeneratorConfig;

    private static RuYiCodeUtil codeUtil;

    //endregion

    //region PostConstruct

    @PostConstruct
    public void init() {
        codeUtil = this;
        codeUtil.datasourceConfig = this.datasourceConfig;
        codeUtil.directoryConfig = this.directoryConfig;
        codeUtil.codeGeneratorRepository = this.codeGeneratorRepository;
        codeUtil.codeGeneratorConfig = this.codeGeneratorConfig;
    }

    //endregion

    //region 自动生成代码

    /**
     * 自动生成代码
     *
     * @param tables     表名
     * @param layoutMode 布局方式
     * @return 文件夹编号
     */
    public static String AutoGenerateCode(List<String> tables, int layoutMode) throws IOException {
        String id = UUID.randomUUID().toString();

        //region 生成后端代码

        //region 删除旧代码

        String codePath = codeUtil.directoryConfig.getTempPath() + "\\src";
        if (FileUtil.exist(codePath)) {
            //删除旧代码
            FileUtil.del(codePath);
        }

        //endregion

        //region 生成新代码

        String javaPath = codeUtil.directoryConfig.getTempPath() + "\\src\\main\\java";
        String mapperPath = codeUtil.directoryConfig.getTempPath() + "\\src\\main\\resources\\mapper";

        FastAutoGenerator.create(
                codeUtil.datasourceConfig.getUrl(),
                codeUtil.datasourceConfig.getUsername(),
                codeUtil.datasourceConfig.getPassword())
                .globalConfig(builder -> {
                    builder.author("RuYiAdmin")//作者
                            .outputDir(javaPath)//输出路径(写到java目录)
                            .enableSwagger()//开启swagger
                            .commentDate("yyyy-MM-dd")
                            .fileOverride();//开启覆盖之前生成的文件
                })
                .packageConfig(builder -> {
                    builder.parent("com.ruyiadmin")
                            .moduleName("code")
                            .entity("domain")
                            .service("service")
                            .serviceImpl("serviceImpl")
                            .controller("controller")
                            .mapper("repository")
                            .xml("mapper")
                            .pathInfo(Collections.singletonMap(OutputFile.xml, mapperPath));
                })
                .strategyConfig(builder -> {
                    builder.addInclude(tables)
                            .addTablePrefix("p_")
                            .serviceBuilder()
                            .formatServiceFileName("I%sService")
                            .formatServiceImplFileName("%sServiceImpl")
                            .entityBuilder()
                            .enableLombok()
                            .logicDeleteColumnName("ISDEL")
                            .enableTableFieldAnnotation()
                            .controllerBuilder()
                            .formatFileName("%sController")
                            .enableRestStyle()
                            .mapperBuilder()
                            .enableBaseResultMap()  //生成通用的resultMap
                            .superClass(BaseMapper.class)
                            .formatMapperFileName("I%sRepository")
                            .enableMapperAnnotation()
                            .formatXmlFileName("%sMapper");
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();

        //endregion

        //endregion

        //region 生产前端代码

        //region 获取元数据信息

        List<DbSchemaEntityDTO> schemaEntityDTOS = new ArrayList<>();

        List<String> tempTables = new ArrayList<>();
        tables.forEach(item -> {
            tempTables.add("'" + item + "'");
        });

        List<DbSchemaInfoDTO> schemaInfoDTOS = codeUtil.codeGeneratorRepository.
                queryMySqlSchemaColumnInfo(tempTables);

        for (DbSchemaInfoDTO schemaInfo : schemaInfoDTOS) {
            if (schemaEntityDTOS.size() <= 0 || schemaEntityDTOS.stream()
                    .filter(t -> t.getEntityName().equals(schemaInfo.getTableName())).count() <= 0) {
                DbSchemaEntityDTO schemaEntity = new DbSchemaEntityDTO(schemaInfo.getTableName());

                DbSchemaFieldDTO schemaField = new DbSchemaFieldDTO();
                schemaField.setColumnName(schemaInfo.getColumnName().toUpperCase());
                schemaField.setDataType(schemaInfo.getColumnDataType());
                schemaField.setColumnComment(schemaInfo.getColumnComment());
                schemaField.setIsNullable(schemaInfo.getIsNullable());
                schemaField.setCharacterMaximumLength(schemaInfo.getCharacterMaximumLength());

                schemaEntity.getFields().add(schemaField);

                schemaEntityDTOS.add(schemaEntity);
            } else {
                DbSchemaEntityDTO schemaEntity = schemaEntityDTOS.stream()
                        .filter(t -> t.getEntityName().equals(schemaInfo.getTableName()))
                        .collect(Collectors.toList())
                        .get(0);

                DbSchemaFieldDTO schemaField = new DbSchemaFieldDTO();
                schemaField.setColumnName(schemaInfo.getColumnName().toUpperCase());
                schemaField.setDataType(schemaInfo.getColumnDataType());
                schemaField.setColumnComment(schemaInfo.getColumnComment());
                schemaField.setIsNullable(schemaInfo.getIsNullable());
                schemaField.setCharacterMaximumLength(schemaInfo.getCharacterMaximumLength());

                schemaEntity.getFields().add(schemaField);
            }
        }

        //endregion

        //region 生成视图层

        //region 获取文件名

        String fileName;
        if (layoutMode == 1) {
            fileName = "template_view_layout_left_right";
        } else {
            fileName = "template_view_layout_up_down";
        }

        //endregion

        //region 获取模板内容

        String path = "templates\\" + fileName;
        ClassPathResource classPathResource = new ClassPathResource(path);
        String strView = FileUtil.readUtf8String(classPathResource.getFile());

        String viewPath = codePath + "\\vue";

        //endregion

        for (DbSchemaEntityDTO schemaEntity : schemaEntityDTOS) {
            String entityName = schemaEntity.toCamelLetters();
            Map<String, Object> map = new HashMap<>();

            //region 处理版权信息

            map.put("EntityName", entityName);
            map.put("Copyright", LocalDateTimeUtil.now().getYear() + " RuYiAdmin All Rights Reserved");
            map.put("Version", SystemUtil.getJavaRuntimeInfo().getVersion());
            map.put("Author", "auto generated by RuYiAdmin Template Code Generator");
            map.put("DateTime", LocalDateTimeUtil.now().toString());

            //endregion

            //region 处理Form字段

            StringBuilder stringBuilder = new StringBuilder();
            List<String> ignoreFields = Arrays.asList(codeUtil.codeGeneratorConfig.getFieldsIgnoreCase()
                    .split(","));

            for (DbSchemaFieldDTO field : schemaEntity.getFields()) {
                if (ignoreFields.stream().noneMatch(t -> t.equals(field.getColumnName()))) {
                    stringBuilder.append(StrUtil.format("{}: null,", field.getColumnName()
                            .toLowerCase()));
                }
            }

            map.put("Fields", stringBuilder);

            //endregion

            //region 处理Grid字段

            stringBuilder = new StringBuilder();

            for (DbSchemaFieldDTO field : schemaEntity.getFields()) {
                if (ignoreFields.stream().noneMatch(t -> t.equals(field.getColumnName()))
                        && !field.getDataType().equals("Guid")) {
                    if (field.getDataType().equals("int")) {
                        if (field.getColumnComment().contains("0:") || field.getColumnComment().contains("0：")) {
                            //region 处理整型枚举

                            String[] arr = field.getColumnComment().split(",");
                            if (arr.length == 1) {
                                arr = field.getColumnComment().split("，");
                            }

                            List<KeyValue> kvs = getKeyValueList(field);

                            stringBuilder.append("<el-table-column ")
                                    .append(StrUtil.format("label = '{}' ", arr[0]))
                                    .append(StrUtil.format("prop = '{}' ", field.getColumnName().toLowerCase()))
                                    .append("align='center'").append(" > ")
                                    .append("<template slot-scope='scope'>");

                            for (int m = 0; m < kvs.size(); m++) {
                                if (m == 0) {
                                    stringBuilder.append(
                                            StrUtil.format("<el-tag v-if='scope.row.{} === {}' type='success'>",
                                                    field.getColumnName().toLowerCase(), kvs.get(m).key))
                                            .append(StrUtil.format("{}", kvs.get(m).value))
                                            .append("</el-tag>");
                                } else {
                                    stringBuilder.append(
                                            StrUtil.format("<el-tag v-else-if='scope.row.{} === {}' type='success'>",
                                                    field.getColumnName().toLowerCase(), kvs.get(m).key))
                                            .append(StrUtil.format("{}", kvs.get(m).value))
                                            .append("</el-tag>");
                                }
                            }

                            stringBuilder.append("</template>" + "</el-table-column>");

                            //endregion
                        }
                    } else {
                        stringBuilder.append("<el-table-column ")
                                .append(StrUtil.format("label = '{}' ", field.getColumnComment()))
                                .append(StrUtil.format("prop = '{}' ", field.getColumnName().toLowerCase()))
                                .append("align='center'").append(" /> ");
                    }
                }
            }

            map.put("GridColumns", stringBuilder);

            //endregion

            //region 处理FormItems

            stringBuilder = new StringBuilder();

            List<DbSchemaFieldDTO> list = new ArrayList<>();
            for (DbSchemaFieldDTO field : schemaEntity.getFields()) {
                if (ignoreFields.stream().noneMatch(t -> t.equals(field.getColumnName()))
                        && !field.getDataType().equals("Guid")) {
                    list.add(field);
                }
            }

            if (list.size() > 0) {
                boolean isOdd = list.size() % 2 == 1;

                for (int i = 0; i < list.size(); i++) {
                    if (isOdd && i == list.size() - 1) {
                        stringBuilder.append("<el-row>")
                                .append("<el-col span='24'>")
                                .append(StrUtil.format("<el-form-item label='{}' prop='{}'>",
                                        getFieldComment(list.get(i)), list.get(i).getColumnName().toLowerCase()
                                ))
                                .append(getInputControl(list.get(i)))
                                .append("</el-form-item>")
                                .append("</el-col>")
                                .append("</el-row>");
                    } else {
                        stringBuilder.append("<el-row>")
                                .append("<el-col span='12'>")
                                .append(StrUtil.format("<el-form-item label='{}' prop='{}'>",
                                        getFieldComment(list.get(i)), list.get(i).getColumnName().toLowerCase()
                                ))
                                .append(getInputControl(list.get(i)))
                                .append("</el-form-item>")
                                .append("</el-col>")
                                .append("<el-col span='12'>")
                                .append(StrUtil.format("<el-form-item label='{}' prop='{}'>",
                                        getFieldComment(list.get(i + 1)), list.get(i + 1).getColumnName().toLowerCase()
                                ))
                                .append(getInputControl(list.get(i + 1)))
                                .append("</el-form-item>")
                                .append("</el-col>")
                                .append("</el-row>");
                        i++;
                    }
                }
            }

            map.put("FormItems", stringBuilder);

            //endregion

            //region 处理FormRules

            stringBuilder = new StringBuilder();

            if (list.size() > 0) {
                for (DbSchemaFieldDTO field : list) {
                    if (field.getIsNullable().equalsIgnoreCase("NO")) {
                        String rule = StringUtils.EMPTY;
                        if (!StringUtils.isEmpty(field.getCharacterMaximumLength())) {
                            rule = StrUtil.format("{}: [【required: true,message: '请输入{}',trigger: 'blur'】," +
                                            "【min: 0,max: {},message: '最大长度{}',trigger: 'blur'】],",
                                    field.getColumnName().toLowerCase(),
                                    field.getColumnComment(),
                                    field.getCharacterMaximumLength(),
                                    field.getCharacterMaximumLength());
                        } else {
                            rule = StrUtil.format("{}: [【required: true,message: '请输入{}',trigger: 'blur'】],",
                                    field.getColumnName().toLowerCase(),
                                    field.getColumnComment());
                        }

                        rule = rule.replace("【", "{").replace("】", "}");
                        stringBuilder.append(rule);
                    }
                }
            }

            map.put("FormRules", stringBuilder);

            //endregion

            //region 生成前端文件

            StringSubstitutor subeditor = new StringSubstitutor(map);
            String viewContent = subeditor.replace(strView);

            String viewDocument = viewPath + "\\" + entityName + "Management.vue";
            FileUtil.newFile(viewDocument);

            FileUtil.writeString(viewContent, viewDocument, Charset.defaultCharset());

            //endregion
        }

        //endregion

        //endregion

        //region 压缩生成代码

        String zipPath = codeUtil.directoryConfig.getTempPath() + "\\" + id + ".zip";
        ZipUtil.zip(codePath, zipPath);

        //endregion

        return id;
    }

    //endregion

    //region 工具私有方法

    //region 获取输入控件

    private static String getInputControl(DbSchemaFieldDTO field) {
        StringBuilder result = new StringBuilder();

        switch (field.getDataType()) {
            case "int":
                if (field.getColumnComment().contains("0:") || field.getColumnComment().contains("0：")) {
                    //region 处理整型枚举

                    String[] arr = field.getColumnComment().split(",");
                    if (arr.length == 1) {
                        arr = field.getColumnComment().split("，");
                    }

                    List<KeyValue> kvs = getKeyValueList(field);

                    result.append(
                            StrUtil.format("<el-select v-model='form.{}' placeholder='请选择{}' filterable clearable > ",
                                    field.getColumnName().toLowerCase(), arr[0]))
                            .append(StrUtil.format("<el-option v-for='item in {}'",
                                    JSON.toJSONString(kvs)
                                            .replace("\"key\"", "key")
                                            .replace("\"value\"", "value")))
                            .append(" :key='item.key' :label='item.value' :value='item.key'>")
                            .append("</el-option>")
                            .append("</el-select>");

                    //endregion
                } else {
                    result.append(
                            StrUtil.format("<el-input-number v-model='form.{}' ",
                                    field.getColumnName().toLowerCase()))
                            .append(StrUtil.format("placeholder='请输入{}' class='colWidth' />",
                                    field.getColumnComment()));
                }
                break;
            case "Double":
            case "float":
            case "decimal":
                result.append(StrUtil.format("<el-input-number v-model='form.{}' ",
                        field.getColumnName().toLowerCase()))
                        .append(StrUtil.format("placeholder='请输入{}' class='colWidth' />",
                                field.getColumnComment()));
                break;
            case "bool":
                result.append(StrUtil.format("<el-radio-group v-model='form.{}'>",
                        field.getColumnName().toLowerCase()))
                        .append("<el-radio :label='0'>备选项0</el-radio>")
                        .append("<el-radio :label='1'>备选项1</el-radio>")
                        .append("</el-radio-group>");
                break;
            case "DateTime":
                result.append(
                        StrUtil.format("<el-date-picker v-model='form.{}' type='datetime' ",
                                field.getColumnName().toLowerCase()))
                        .append(StrUtil.format("placeholder='请选择{}' class='colWidth' >",
                                field.getColumnComment()))
                        .append("</el-date-picker>");
                break;
            case "String":
            default:
                result.append(StrUtil.format("<el-input v-model='form.{}' prefix-icon='el-icon-search' ",
                        field.getColumnName().toLowerCase()))
                        .append(StrUtil.format("placeholder='请输入{}' class='colWidth' />",
                                field.getColumnComment()));
                break;
        }

        return result.toString();
    }

    //endregion

    // region 获取KeyValue集合

    static class KeyValue {
        public Object key;
        public Object value;

        public KeyValue(Object key, Object value) {
            this.key = key;
            this.value = value;
        }

    }

    private static List<KeyValue> getKeyValueList(DbSchemaFieldDTO field) {
        List<KeyValue> kvs = new ArrayList<>();

        String[] arr = field.getColumnComment().split(",");
        if (arr.length == 1) {
            arr = field.getColumnComment().split("，");
        }

        for (String item : arr) {
            if (item.contains(":") || item.contains("：")) {
                String[] kv = item.split(":");
                if (kv.length == 1) {
                    kv = item.split("：");
                }

                if (kv.length == 1) {
                    continue;
                }

                if (NumberUtil.isInteger(kv[0])) {
                    kvs.add(new KeyValue(Integer.parseInt(kv[0]), kv[1]));
                }
            }
        }

        return kvs;
    }

    //endregion

    //region 获取字段说明

    private static String getFieldComment(DbSchemaFieldDTO field) {
        String result = StringUtils.EMPTY;

        if (field.getColumnComment().contains("0:") || field.getColumnComment().contains("0：")) {
            String[] arr = field.getColumnComment().split(",");
            if (arr.length == 1) {
                arr = field.getColumnComment().split("，");
            }
            result = arr[0];
        } else {
            result = field.getColumnComment();
        }

        return result;
    }

    //endregion

    //endregion

}
